#pragma once

#include "Fish.h"
#include "FishFactory.h"
#include "Point.h"
#include <sstream>
#include <iostream>


using namespace std;

// give fish a unique id
int Fish::counter = 1;

// CTOR
Fish::Fish(){
	this->id = Fish::counter;
	this->fishImpl = 0;
	Fish::counter++;
	// Observer's subject
	this->sbj = 0;
}

// COPY-CTOR
//Fish::Fish(const Fish & fish){}


const Fish& Fish::operator=(const Fish & fish){
	if (this != &fish){
		this->fishImpl = FishFactory::CopyFish(*fish.fishImpl);
	}

	return *this;
}

Fish::~Fish(){
	delete fishImpl;
	fishImpl = 0;

	if (this->sbj != 0){
		this->sbj->Detach(this);
		this->sbj = 0;
	}
}

void Fish::create(const FishFactory::FISH_TYPE& impl, Point& speed, const Point& location, bool transparent, int size){
	try {
		this->fishImpl = FishFactory::CreateFish(impl, speed, location, transparent, size);
	}
	catch (bad_alloc& ba){
		// on allocation error - set the ptr to 0 and rethrow the exception
		this->fishImpl = 0;
		throw;
	}
}
void Fish::copy(const Fish& fish){
	try {
		this->fishImpl = FishFactory::CopyFish(*fish.fishImpl);
	}
	catch (bad_alloc& ba){
		// on allocation error - set the ptr to 0 and rethrow the exception
		this->fishImpl = 0;
		throw;
	}
}

// Observer methods
void Fish::Update(Subject* ChngSubject, const std::string& message){
	// if any ptr is 0 or this is not the right subject - do nothing
	if ((ChngSubject == 0) || (this->sbj == 0) || (this->sbj != ChngSubject)) {
		return;
	}
	// check the message is right
	if (message.compare("feed") == 0){
		this->feed();
	}
	if (message.compare("debug") == 0){
		cout << this->getStats();
	}
	if (message.compare("pause") == 0){
		this->pause();
	}
}

void Fish::setSubject(Subject *_sbj){
	if (this->sbj != _sbj){
		this->sbj = _sbj;
		this->sbj->Attach(this);
	}
}
Subject * Fish::getSubject(Subject *sbj){
	return this->sbj;
}


// Fish object methods

std::string Fish::getStats(){
	std::stringstream str;
	str << "fish no: "<<this->getID() << " "<<this->fishImpl->getTypeName()<<" at: "<<this->getLocation().getX()<<","<<
		this->getLocation().getY()<<" vel: "<<this->getSpeed().getX() <<","<<this->getSpeed().getY() <<
		" size: "<<this->getSize();
	if (this->isTransparent())
		str << " transparent";
	else
		str << " visible";
	str << "\n";
	return str.str();
}

int Fish::getID() const	{
	return this->id; 
}

bool Fish::initialized() const {
	return (this->fishImpl != 0); 
}


// FishImpl delegation methods
// if the fishImpl field is not initialized (is 0) there is no guarantee for the returned value of these methods
// use bool Fish::initialized() to verifiy the object is good for use

void Fish::feed() {
	if (fishImpl != 0) fishImpl->feed(); 
}

void Fish::pause() {
	if (fishImpl != 0) fishImpl->pause(); 
}

const Point& Fish::getSpeed() const {
	if (fishImpl != 0) return fishImpl->getSpeed();
	else return  Point::Point(0,0);
}

// set new speed - specify speed in each coord. and direction using +/-
void Fish::setSpeed(const Point& speed) {
	if (fishImpl != 0) fishImpl->setSpeed(speed);
}

const Point& Fish::getLocation() const {
	if (fishImpl != 0) return fishImpl->getLocation();
}

void Fish::setLocation(const Point& location) {
	if (fishImpl != 0) fishImpl->setLocation(location);
}

bool Fish::isTransparent() const {
	if (fishImpl != 0) return fishImpl->isTransparent();
}

void Fish::setTransparent(bool trans) {
	if (fishImpl != 0) fishImpl->setTransparent(trans);
}

int Fish::getSize() const {
	if (fishImpl != 0) return fishImpl->getSize();
}

void Fish::setSize(int size) {
	if (fishImpl != 0) fishImpl->setSize(size); 
}
/*
// class Fish Tests 
#include <iostream>
int main(){
	Fish salmon;
	Fish goldFish;
	
	try {
		salmon.create(FishFactory::SALMON, Point(rand()*100,rand()*100), Point(rand()*100,rand()*100),false, 1);
		goldFish.create(FishFactory::GOLD_FISH, Point(rand()*100,rand()*100), Point(rand()*100,rand()*100),false, 5);
	}
	catch (bad_alloc& ba) {
		cout << "mem. allocation error" << endl;
		exit(1);
	}
	if (salmon.initialized()){
		cout << "salmon is initialized" << endl;
	}
	if (goldFish.initialized()){
		cout << "goldFish is initialized" << endl;
	}
	salmon.feed();
	cout << salmon.getID() << endl;
	salmon.getLocation();
	cout << salmon.getSize() << endl;
	salmon.getSpeed();
	cout << salmon.isTransparent() << endl;
	salmon.pause();
	goldFish.feed();
	

	cout << "end of tests" << endl;
	string input;
	getline(cin,input);

	return 0;
}

*/